<?php

/**
 * This file is part of Webman AI.
 *
 * @author    walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link      http://www.workerman.net/
 */


namespace plugin\ai\app\handler\driver;

use Exception;
use support\exception\BusinessException;
use Workerman\Http\Client;
use Workerman\Http\Response;

class Ernie extends Base
{

    /**
     * @var string api地址
     */
    protected $api = 'https://aip.baidubce.com';

    /**
     * @var string secretKey
     */
    protected $secretKey = '';

    /**
     * @param $options
     */
    public function __construct($options)
    {
        parent::__construct($options);
        $this->secretKey = $options['secretKey'] ?? '';
    }

    /**
     * 对话
     * @param $data
     * @param $options
     * @return void
     * @throws BusinessException
     */
    public function completions($data, $options)
    {
        $headers = $this->getHeaders($options);
        if (isset($options['stream'])) {
            $data['stream'] = true;
        }
        $options = $this->formatOptions($options);
        $data['messages'] = static::formatMessages($data['messages']);
        $requestOptions = [
            'method' => 'POST',
            'data' => json_encode($data),
            'headers' => $headers,
            'progress' => function ($buffer) use ($options) {
                static $tmp = '';
                $tmp .= $buffer;
                $len = strlen($tmp);
                if ($len < 1) {
                    return;
                }
                if ($tmp[$len - 1] !== "\n") {
                    return;
                }
                preg_match_all('/data: (\{.+?\})\n/', $tmp, $matches);
                $tmp = '';
                foreach ($matches[1]?:[] as $match) {
                    $json = json_decode($match, true);
                    if (!$json) {
                        continue;
                    }
                    $data = array_merge(['content' => $json['result'] ?? ''], $json);
                    if ($json['error_msg'] ?? false) {
                        $data['error'] = ['code' => $json['error_code'], 'message' => $json['error_msg']];
                    }
                    $options['stream']($data);
                }
            },
            'success' => function (Response $response) use ($options) {
                $options['complete'](static::formatResponse((string)$response->getBody()), $response);
            },
            'error' => function ($exception) use ($options) {
                $options['complete']([
                    'error' => [
                        'code' => 'exception',
                        'message' => $exception->getMessage(),
                        'detail' => (string) $exception
                    ],
                ], new Response(0));
            }
        ];
        $model = strtolower($data['model']);
        $map = [
            'ernie-bot-4' => 'completions_pro',
            'ernie-bot' => 'completions',
            'ernie-bot-turbo' => 'completions',
            'ernie-3.5-8k' => 'completions',
            'ernie-lite-8k' => 'eb-instant',
            'ernie-speed-8k' => 'ernie_speed',
            'ernie-lite-8k-0922' => 'eb-instant',
            'ernie-lite-8k-0308' => 'ernie-lite-8k',
        ];
        $action = $map[$model] ?? $model;
        $path = '/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/' . $action;
        $path .= '?access_token=' . $this->getAccessToken();
        $http = new Client(['timeout' => 600]);
        $http->request($this->api . $path, $requestOptions);
    }

    public function getAccessToken()
    {
        static $accessToken, $apiKey, $secretKey, $lastUpdate = 0;
        if (!$this->apikey || !$this->secretKey) {
            throw new BusinessException('文心一言ApiKey或者secretKey未设置');
        }
        if ($this->apikey !== $apiKey || $this->secretKey !== $secretKey) {
            $accessToken = '';
            $apiKey = $this->apikey;
            $secretKey = $this->secretKey;
        }
        $time = time();
        if (!$accessToken || $time - $lastUpdate > 60 * 60 * 24) {
            $lastUpdate = $time;
            $buffer = file_get_contents("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=$apiKey&client_secret=$secretKey");
            if (!$buffer) {
                throw new BusinessException('access_token获取失败，访问 https://aip.baidubce.com/oauth/2.0/token 失败');
            }
            $json = json_decode($buffer, true);
            if (!$json || !isset($json['access_token'])) {
                throw new BusinessException('access_token获取失败，' . $buffer);
            }
            $accessToken = $json['access_token'];
        }
        return $accessToken;
    }

    public static function formatResponse($buffer)
    {
        $json = json_decode($buffer, true);
        if ($json) {
            if (isset($json['error_msg'])) {
                return [
                    'error' => [
                        'code' => $json['error_code'] ?? 500,
                        'message' => $json['error_msg'],
                        'detail' => $buffer
                    ]
                ];
            }
            if ($content = $json['result'] ?? '') {
                return $content;
            }
            return $buffer;
        }
        $chunks = explode("\n", $buffer);
        $content = '';
        foreach ($chunks as $chunk) {
            if ($chunk === "") {
                continue;
            }
            $chunk = trim(substr($chunk, 6));
            if ($chunk === "" || $chunk === "[DONE]") {
                continue;
            }
            try {
                $data = json_decode($chunk, true);
                if (isset($data['error_msg'])) {
                    $content .= $data['error_msg'] ?? "";
                } else {
                    $content .= $data['result'] ?? "";
                }
            } catch (Exception $e) {
                echo $e;
            }
        }
        return $content;
    }
}